﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Minestorm.Retro.Entities;
using Microsoft.Xna.Framework.Graphics;
using Minestorm;
using Microsoft.Xna.Framework.Audio;

namespace Minestorm.Retro.GameLogic
{
    public class LevelStateManager
    {
        TimeSpan _lastFired = TimeSpan.Zero;
        EntityManager _entityManager;
        GraphicsDevice _graphics;
        private LevelPhase _phase;
        private LevelSettings _settings;
        TextureManager _textureManager;
        SoundManager _soundManager;
        private GameTime _gameTime;
        SoundEffectInstance _thrustSoundInstance;
        private PointSpriteGenerator _pointSpriteGenerator;

        public LevelStateManager(ScreenManager screenManager, int score, LevelSettings settings, TextureManager textureManager, SoundManager soundManager)
        {
            _entityManager = new EntityManager();
            _graphics = screenManager.Game.GraphicsDevice;
            _phase = LevelPhase.Deploy;
            _settings = settings;
			ScreenManager = screenManager;
            _textureManager = textureManager;
            _soundManager = soundManager;

            _thrustSoundInstance = _soundManager.GetSoundEffect("thrust_sound").CreateInstance();
            if(!_thrustSoundInstance.IsLooped)
                _thrustSoundInstance.IsLooped = true;

            Score = score;

            _pointSpriteGenerator = new PointSpriteGenerator(_graphics, _textureManager, _entityManager);
        }

        public int Score { get; set; }

		public ScreenManager ScreenManager { get; set; }
        public Ship GetShip()
        {
            return _entityManager.GetShip();
        }

        public bool DisplayLevel { get; private set; }

        public IEnumerable<Entity> GetAllEntities()
        {
            return _entityManager.GetAllEntities();
        }

        public void Teleport(GameTime gameTime)
        {
            var ship = _entityManager.GetShip();

            ship.Position = RandomHelper.GetSpawnVectorWithinBounds();

            _soundManager.Play("spawn_sound");

            _pointSpriteGenerator.Start(ship.Position, 1, .2f, 2f, 0, 1f, 1f, true, TimeSpan.FromSeconds(.5f), 100, 0, gameTime.TotalGameTime);
            ship.Teleport();
        }
        
        public void Fire()
        {
            var ship = _entityManager.GetShip();

            if (ship == null)
                return;

            if (!ship.IsFiring)
                return;

            var position = new Vector2(ship.Position.X + (float)Math.Sin(-ship.Direction) * Constants.Ship.BulletOffset, ship.Position.Y + (float)Math.Cos(-ship.Direction) * Constants.Ship.BulletOffset);

            _entityManager.AddEntity(new Bullet(_graphics, position, ship.Direction, _gameTime.TotalGameTime, _textureManager));

            _lastFired = _gameTime.TotalGameTime;

            _soundManager.Play("fire_sound");
        }

        public void MoveShip()
        {
            var ship = _entityManager.GetShip();

            if (ship == null)
                return;
            
            ship.Move(_phase == LevelPhase.Play || _phase == LevelPhase.Spawn, _gameTime);
        }

        public void MoveFireballs()
        {
            var ship = _entityManager.GetShip();

            foreach (var fireball in _entityManager.GetFireballs())
            {
                if (ship.IsAlive)
                {
					if(fireball.CheckLineCollision(fireball.LastPosition, fireball.Position, ship.Verticies.Where((v, i) => i < 7).Select(v => Vector3.Transform(v.Position, ship.GetWorld("ship"))).ToArray()))
                    {
                        ship.Kill(_gameTime);
                        _entityManager.AddEntity(new Explosion(_graphics, ship.Position));
                        fireball.ShouldDraw = false;
                        _phase = LevelPhase.Dead;
                        _soundManager.Play("explode_sound");
                        _thrustSoundInstance.Stop();
                    }
                }
				foreach (var bullet in _entityManager.GetBullets())
				{
					if (fireball.Position == fireball.LastPosition) continue;
					Vector2 dotProd = fireball.LastPosition - fireball.Position;
					Vector2 normal = new Vector2(-dotProd.Y, dotProd.X);
					normal.Normalize();
					dotProd.Normalize();
					dotProd = dotProd * 0.5f;
					Vector2 p1 = fireball.Position + (normal * 0.5f) - dotProd;
					Vector2 p2 = fireball.Position + (normal * -0.5f) - dotProd;
					Vector2 p3 = fireball.LastPosition + (normal * 0.5f) - dotProd;
					Vector2 p4 = fireball.LastPosition + (normal * -0.5f) - dotProd;

					if (fireball.CheckLineCollision(p1, p2, new Vector2[] { bullet.LastPosition, bullet.Position }) ||
						fireball.CheckLineCollision(p3, p4, new Vector2[] { bullet.LastPosition, bullet.Position })
						)
					{
						bullet.ShouldDraw = false;
						fireball.ShouldDraw = false;
                        _entityManager.AddEntity(new Explosion(_graphics, fireball.Position));
                        _soundManager.Play("explode_sound");
                        Score += fireball.Points;
					}
				}

                if (fireball.ShouldDraw)
                    fireball.Move();
            }
        }

        private bool _hadMineLayer = false;
        private int _newStarCount = 0;

        private TimeSpan? _mineLayerCountdown;

        public void MoveMinelayer()
        {
            var mineLayer = _entityManager.GetMineLayer();
            var ship = _entityManager.GetShip();
            
            if (mineLayer == null && _phase != LevelPhase.Finished)
            {
                if (_hadMineLayer)
                    return;

                if (_entityManager.GetStars().Length > 0)
                    return;

                if (_mineLayerCountdown == null)
                {
                    _mineLayerCountdown = _gameTime.TotalGameTime;
                    return;
                }

                if (_gameTime.TotalGameTime - _mineLayerCountdown < TimeSpan.FromSeconds(2))
                    return;

                _hadMineLayer = true;

                _entityManager.AddEntity(mineLayer = new MineLayer(_graphics, RandomHelper.MineLayerSpawn()));

                return;
            }

            if (mineLayer != null && mineLayer.ShouldDraw && _phase != LevelPhase.Finished)
            {
                if (mineLayer.LayingStar)
                {
                    var newStar = new Star(_graphics, mineLayer.Position, _textureManager);

                    newStar.Hidden = false;

                    newStar.AutoSpawn = _newStarCount++ % 7 == 0;

                    _entityManager.AddEntity(newStar);
                }

                int vectorIndex;

                if (mineLayer.CheckPointCollision(ship.Verticies.Where((v, i) => i < 7).Select(v => Vector3.Transform(v.Position, ship.GetWorld("ship"))).ToArray(), out vectorIndex))
                {
                    ship.Kill(_gameTime);
                    mineLayer.ShouldDraw = false;
                    _entityManager.AddEntity(new Explosion(_graphics, ship.Position));
                    _entityManager.AddEntity(new Explosion(_graphics, mineLayer.Position));
                    _phase = LevelPhase.Dead;
                    _soundManager.Play("explode_sound");
                    _thrustSoundInstance.Stop();
                }

                foreach (var bullet in _entityManager.GetBullets())
                {
                    if (mineLayer.CheckLineCollision(bullet.LastPosition, bullet.Position))
                    {
                        bullet.ShouldDraw = false;
                        mineLayer.ShouldDraw = false;
                        _entityManager.AddEntity(new Explosion(_graphics, mineLayer.Position));
                        _soundManager.Play("explode_sound");
                        Score += mineLayer.Points;
                    }
                }

                mineLayer.Move();
            }

            if (_hadMineLayer)
            {
                foreach(var star in _entityManager.GetStars())
                    if (star.ShouldAutoSpawn(_gameTime))
                    {
                        var mine = SpawnMine(RandomHelper.GetElement(_settings.MineTypes), MineSize.Large, star);

                        mine.AutoSpawned = true;
                    }
            }
        }

        public void PlayThrust()
        {
            switch (_thrustSoundInstance.State)
            {
                case SoundState.Playing:
                    return;
                case SoundState.Paused:
                    _thrustSoundInstance.Resume();
                    break;
                case SoundState.Stopped:
                    _thrustSoundInstance.Play();
                    break;
            }
        }

        public void StopThrust(bool tryPause)
        {
            StopThrust(tryPause, true);
        }
        public void StopThrust(bool tryPause, bool playEnd)
        {
            switch (_thrustSoundInstance.State)
            {
                case SoundState.Stopped:
                case SoundState.Paused:
                    return;
                case SoundState.Playing:
                    if (tryPause)
                        _thrustSoundInstance.Pause();
                    else
                        _thrustSoundInstance.Stop();

                    if(playEnd)
                        _soundManager.Play("thrustend_sound");
                    break;
            }
        }

        public void MoveMines()
        {
            var ship = _entityManager.GetShip();

            if (ship == null)
                return;

            int vectorIndex;

            foreach (var mine in _entityManager.GetMines())
            {
                if (ship.IsAlive)
                {
                    if (mine.CheckPointCollision(ship.Verticies.Where((v, i) => i < 7).Select(v => Vector3.Transform(v.Position, ship.GetWorld("ship"))).ToArray(), out vectorIndex))
                    {
                        ship.Kill(_gameTime);
                        mine.ShouldDraw = false;
                        _entityManager.AddEntity(new Explosion(_graphics, ship.Position));
                        _entityManager.AddEntity(new Explosion(_graphics, mine.Position));
                        _phase = LevelPhase.Dead;
                        _soundManager.Play("explode_sound");
                        _thrustSoundInstance.Stop();
                    }

                    foreach (var bullet in _entityManager.GetBullets())
                    {
                        if (mine.CheckLineCollision(bullet.LastPosition, bullet.Position))
                        {
                            bullet.ShouldDraw = false;
                            SpawnChildren(mine);
                            SpawnFireball(mine);
                            mine.ShouldDraw = false;
                            _entityManager.AddEntity(new Explosion(_graphics, mine.Position));
                            _soundManager.Play("explode_sound");
                            Score += mine.Points;
                        }
                    }
                }

                if (mine.ShouldDraw && mine.ShouldMove(_gameTime))
                    mine.Move(ship.IsAlive ? new Vector2?(ship.Position) : null);
            }
        }

        public void CheckNewStars()
        {
            //foreach (var star in _entityManager.GetStars())
            //{
            //    //if (gameTime.TotalGameTime - star.BornOn > TimeSpan.FromSeconds(2))
            //    //{
            //    //    var mine = CreateMine(RandomHelper.GetElement(_settings.MineTypes), MineSize.Large, star.Position, RandomHelper.GetAngle());
                    
            //    //    _entityManager.AddEntity(mine);

            //    //    star.ShouldDraw = false;
            //    //}
            //}
        }

        public void MoveBullets()
        {
            foreach (var bullet in _entityManager.GetBullets())
            {
                bullet.Move();

                if (_gameTime.TotalGameTime - bullet.Born > Constants.Ship.BulletLifeTimespan)
                    bullet.ShouldDraw = false;
            }
        }

        public bool CheckWin()
        {
            if (_entityManager.GetMines().Length == 0 && _entityManager.GetStars().Length == 0 && _entityManager.GetFireballs().Length == 0)
            {
                if (_entityManager.GetShip().IsAlive)
                {
                    //_entityManager.GetShip().WinTimespan = _gameTime.TotalGameTime;
                    _thrustSoundInstance.Stop();

                    var mineLayer = _entityManager.GetMineLayer();

                    if (mineLayer != null)
                        mineLayer.ShouldDraw = false;

                    return true;
                }
            }

            return false;
        }

        public bool CheckDeadTimer()
        {
            return _gameTime.TotalGameTime - _entityManager.GetShip().EndTimespan > Constants.Level.DeadTimeoutTimespan;
        }

        public bool CheckWinTimer()
        {
            return _gameTime.TotalGameTime - _entityManager.GetShip().WinTimespan > Constants.Level.WinTimeoutTimespan;
        }

        public LevelPhase Phase { get { return _phase; } set { _phase = value; } }

        private TimeSpan _lastSpawn = TimeSpan.Zero;
        private int _totalSpawnIndex = 0;
        private int _spawnPhaseSpawnIndex = 0;

        public void BeginUpdate(GameTime gameTime)
        {
            _entityManager.Update(_gameTime = gameTime);
        }

        public void SpawnChildren(Mine mine)
        {
            if (!mine.ShouldDraw)
                return;

            if (mine.MineSize == MineSize.Small)
                return;

            for (var i = 0; i < 2; i++)
            {
                Star star = null;

                if (mine.AutoSpawned)
                    star = _entityManager.GetStar(s => !s.AutoSpawn);
                else
                    star = _entityManager.GetStar(_totalSpawnIndex++);

                if (star == null)
                    return;

                var newMine = SpawnMine(mine.MineType, (MineSize)((int)mine.MineSize + 1), star);

                newMine.AutoSpawned = mine.AutoSpawned;
            }
        }

        public void SpawnFireball(Mine mine)
        {
            if (!mine.ShouldDraw)
                return;

            if (!mine.HasFireball)
                return;

            var fireball = new Fireball(_graphics, mine.Position, _entityManager.GetShip().Position);

            _entityManager.AddEntity(fireball);
        }

        public void UpdatePointSprites(GameTime gameTime)
        {
            _pointSpriteGenerator.Update(gameTime);
        }

        public void DrawPointSprites(GameTime gameTime)
        {
            _pointSpriteGenerator.Draw(gameTime);
        }

        public void LoadDeployment(GameTime gameTime)
        {
            _entityManager.AddEntity(new Ship(_graphics, Vector2.Zero, _pointSpriteGenerator));
            _entityManager.AddEntity(new Dropship(_graphics, new Vector2(0, 25)));

            var starCount = _settings.StarCount;

            for (var i = 0; i < starCount; i++)
            {
                var pos = RandomHelper.GetSpawnVectorWithinBounds();

                _entityManager.AddEntity(new Star(_graphics, new Vector2(0, pos.Y), pos, _textureManager));
            }

        }

        public void SpawnParentMines()
        {
            if (_gameTime.TotalGameTime - _lastSpawn < Constants.Level.SpawnIntervalTimespan)
                return;

            var star = _entityManager.GetStar(_totalSpawnIndex++);

            SpawnMine(_settings.MineTypes[_spawnPhaseSpawnIndex], MineSize.Large, star);

            _spawnPhaseSpawnIndex++;

            _lastSpawn = _gameTime.TotalGameTime;

            if (_spawnPhaseSpawnIndex == _settings.MineTypes.Length)
                _phase = LevelPhase.Play;
        }

        private Mine SpawnMine(MineType mineType, MineSize mineSize, Star star)
        {
            var mine = CreateMine(mineType, mineSize, star.Position, RandomHelper.GetAngle());

            _entityManager.AddEntity(mine);

            star.ShouldDraw = false;

            if (mineSize == MineSize.Large)
                _soundManager.Play("spawn_sound");

            return mine;
        }

        private Mine CreateMine(MineType type, MineSize size, Vector2 position, float angle)
        {
            switch (type)
            {
                case MineType.Magnetic:
                    return new MagneticMine(_graphics, size, position, angle);
                case MineType.Fireball:
                    return new FireballMine(_graphics, size, position, angle);
                case MineType.MagneticFireball:
                    return new MagneticFireballMine(_graphics, size, position, angle);
                default:
                    return new FloatingMine(_graphics, size, position, angle);
            }
        }

        public void UpdateExplosions()
        {
            foreach (var explosion in _entityManager.GetExplosions())
                explosion.Move();
        }

        public void UpdateStars()
        {
            var allDeployed = true;

            foreach (var star in _entityManager.GetStars())
            {
                if (!star.Hidden && star.Deploying)
                {
                    var change = star.FinalPosition.X < 0f ? -Constants.Star.DeploySpeed : Constants.Star.DeploySpeed;

                    star.Position = new Vector2(star.Position.X + change, star.Position.Y);

                    if (Math.Abs(star.Position.X) >= Math.Abs(star.FinalPosition.X))
                    {
                        star.Position = star.FinalPosition;
                        star.Deploying = false;

                        allDeployed = false;
                    }
                }
                else if (star.Hidden)
                    allDeployed = false;
            }

            if (allDeployed && _phase == LevelPhase.Deploy && !_entityManager.HasDropship() && !_entityManager.GetShip().Hidden)
            {
                _lastSpawn = _gameTime.TotalGameTime + Constants.Level.SpawnStartTimespan;
                _phase = LevelPhase.Spawn;
            }
        }

        public void UpdateDropship()
        {
            var dropship = _entityManager.GetDropship();

            if (dropship == null)
                return;

            dropship.Position = new Vector2(0, dropship.Position.Y + Constants.Dropdship.MovementStep);
            dropship.Scale += Constants.Dropdship.ScaleStep;

            foreach (var star in _entityManager.GetStars())
                if (star.Hidden && !star.Deploying)
                    if (star.Position.Y > dropship.Position.Y)
                    {
                        star.Hidden = false;
                        star.Deploying = true;
                    }

            if (dropship.Scale < Constants.Dropdship.MinScale)
            {
                dropship.ShouldDraw = false;

                _entityManager.GetShip().Hidden = false;

                DisplayLevel = true;
            }
        }
    }
}
